package br.com.intelsys.iflow.driver.struts;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import br.com.intelsys.iflow.IFlow;
import br.com.intelsys.iflow.IFlowInstance;
import br.com.intelsys.iflow.jbpm.IFlowJbpm;

public class StrutsFlowDriver implements IFlowStruts {
	private IFlow iFlow;
	private String config = "iflow-struts.xml";
	private static CfgIFlowStruts cfgIFlowStruts;
	private static final Log log = LogFactory.getLog(IFlowJbpm.class);
	
	static CfgIFlowStruts getCfgIFlowStruts() {
		return(StrutsFlowDriver.cfgIFlowStruts);
	}
	
	
	public void setIFlow(IFlow iFlow) {
		this.iFlow = iFlow;
	}
	
	public void setConfig(String config) {
		this.config = config;
	}

	public IFlowInstanceStruts createInstance(String processName, IFlowInstance iFlowInstance) {
		loadConfig();
		return(new IFlowInstanceStrutsDriver(processName, iFlowInstance));
	}

	public IFlowInstanceStruts createInstance(String processName) {
		loadConfig();
		return(new IFlowInstanceStrutsDriver(processName, this.iFlow.createInstance(processName)));
	}

	public IFlowInstanceStruts getInstance(String processName, long processInstanceId) {
		loadConfig();
		return(new IFlowInstanceStrutsDriver(processName, this.iFlow.getInstance(processInstanceId)));
	}
	
	public List <IFlowInstanceStruts> getInstanceListByStateName(String processName, String stateName) {
		List <String> stateNameList = new ArrayList <String> (1);
		stateNameList.add(stateName);
		return(this.getInstanceListByStateName(processName, stateNameList));
	}

	public List <IFlowInstanceStruts> getInstanceListByStateName(String processName, List <String> stateNameList) {
		List <IFlowInstanceStruts> ifisList;
		loadConfig();
		List <IFlowInstance> ifiList = this.iFlow.getInstanceListByStateName(processName, stateNameList);
		if (ifiList != null) {
			ifisList = new ArrayList <IFlowInstanceStruts> (ifiList.size());
			for (IFlowInstance ifi : ifiList) {
				ifisList.add(new IFlowInstanceStrutsDriver(processName, ifi));
			}
		} else {
			ifisList = null;
		}
		return(ifisList);
	}
	

	public void closeContext() {
		loadConfig();
		this.iFlow.closeContext();
	}
	
	
	
	

	/**
	 * Procura pelos arquivos de configuracao
	 * 
	 * @param file Arquivo que ira ser procurado
	 * @return instancia do arquivo
	 */
	private File searchFileConf(String file) {
		String mainXml = null;
		try {
			Enumeration<URL> e = this.getClass().getClassLoader().getResources(file);
			while (e.hasMoreElements()) {
				mainXml = e.nextElement().getPath();
			}
		} catch (IOException e) {
			log.error("Nao foi possivel encontrar o arquivo de configuracao " + file + ". Verifique se ele esta em seu classpath.");
			e.printStackTrace();
		}
		if (mainXml == null) {
			File fileTmp = new File(file);
			if (fileTmp.exists()) {
				mainXml = file;
			} else {
				log.error("Nao foi possivel encontrar o arquivo de configuracao " + file + ". Verifique se ele esta em seu classpath.");
			}
		}
		return ((mainXml != null ? new File(mainXml) : null));
	}

	/**
	 * Carrega as configuracoes do IFlowStruts
	 */
	private void loadConfig() {
		String fileName = null;
		try {
			if (StrutsFlowDriver.cfgIFlowStruts == null) {
				File fileTmp = this.searchFileConf(this.config);
				fileName = fileTmp.getName();
				log.info("Configuracao " + this.config + " encontrada.");
				this.readFactoryManager((new SAXBuilder()).build(fileTmp));
			}
		} catch (NullPointerException e) {
			e.printStackTrace();
			log.error("Nao foi possivel acessar o arquivo de configuracao do iflow-struts " + fileName + ". Verifique se ele esta em seu classpath");
		} catch (IOException e) {
			e.printStackTrace();
			log.error("Nao foi possivel acessar o arquivo de configuracao de iflow-struts " + fileName + ". Verifique se ele esta em seu classpath");
		} catch (JDOMException e) {
			e.printStackTrace();
			log.error("Existe algum erro no arquivo de configuracao de iflow-struts " + fileName);
		}
	}

	
	/**
	 * Faz o parse do arquivo de configuracao do IFlowStruts
	 * 
	 * @param mainDoc SAX XML Document
	 */
	private void readFactoryManager(Document mainDoc) {
		final String TMP_PROCESS = "process";
		final String TMP_TRANSITION = "transition";
		final String TMP_HANDLE = "handle";
		final String TMP_SUCCESS = "success";
		final String TMP_PENDING = "pending";
		final String TMP_PROPERTY = "property";
		final String TMP_NAME = "name";
		final String TMP_FROM = "from";
		final String TMP_VALIDATOR = "validator";
		final String TMP_SPLIT = ",";
		final String TMP_VALUE = "value";
		final String TMP_TYPE = "type";

		Element eChild, eChild1, eChild2, eChild3, eChild4, eChild5;
		List <Element> lChild4, lChild5;
		Iterator<Element> it, it1, it2, it3, it4, it5;
		Element cmail = mainDoc.getRootElement();
		List<Element> elementList = cmail.getChildren();
		it = elementList.iterator();

		StrutsFlowDriver.cfgIFlowStruts = new CfgIFlowStruts();
		CfgProcess cfgProcess;
		CfgTransition cfgTransition;
		CfgHandle cfgHandle;
		CfgHandleAction cfgHandleAction;
		CfgProperty cfgProperty;
		
		String[] fromArr;
		int i;
		
		log.debug("Parseando o config ...");
		while (it.hasNext()) {
			eChild = it.next();
			if (eChild.getName().equals(TMP_PROCESS)) {
				log.debug("Carregando <process> "+eChild.getAttributeValue(TMP_NAME));
				cfgProcess = new CfgProcess(eChild.getAttributeValue(TMP_NAME));
				StrutsFlowDriver.cfgIFlowStruts.addProcess(cfgProcess);
				it1 = eChild.getChildren().iterator();
				eChild1 = it1.next();
				if (eChild1.getName().equals(TMP_TRANSITION)) {
					it2 = eChild1.getChildren().iterator();
					cfgTransition = new CfgTransition(eChild1.getAttributeValue(TMP_NAME).split(TMP_SPLIT));
					log.debug("Carregando <transition> "+eChild1.getAttributeValue(TMP_NAME));
					cfgProcess.addTransition(cfgTransition);
					if (eChild1.getAttributeValue(TMP_VALIDATOR) != null && eChild1.getAttributeValue(TMP_VALIDATOR).length() > 0) {
						log.debug("Adicionando validator para a transation: "+eChild1.getAttributeValue(TMP_VALIDATOR));
						cfgTransition.setTransitionValidatorClassName(eChild1.getAttributeValue(TMP_VALIDATOR));
					}
					while (it2.hasNext()) {
						eChild2 = it2.next();
						if (eChild2.getName().equals(TMP_HANDLE)) {
							//mapHostname.put(eChild2.getAttributeValue(FIELD_NAME), eChild2.getText());
							it3 = eChild2.getChildren().iterator();
							cfgHandle = new CfgHandle();
							cfgTransition.addHandle(cfgHandle);
							log.debug("Adicionando um <handle>");
							if (eChild2.getAttributeValue(TMP_FROM) != null && eChild2.getAttributeValue(TMP_FROM).length() > 0) {
								fromArr = eChild2.getAttributeValue(TMP_FROM).split(TMP_SPLIT);
								for (i=0;i<fromArr.length;i++) {
									log.debug("Adicionando from "+fromArr[i]);
									cfgHandle.addFrom(fromArr[i]);
								}
							}
							while (it3.hasNext()) {
								eChild3 = it3.next();
								
								if (eChild3.getName().equals(TMP_SUCCESS)) {
									cfgHandleAction = new CfgHandleActionSuccess();
									log.debug("Adicionando <success>");
								} else if (eChild3.getName().equals(TMP_PENDING)) {
									cfgHandleAction = new CfgHandleActionPending();
									log.debug("Adicionando <pending>");
								} else {
									cfgHandleAction = null;
								}
								if (cfgHandleAction != null) {
									cfgHandle.addHandle(cfgHandleAction);
									cfgHandleAction.setName(eChild3.getAttributeValue(TMP_NAME));
									cfgHandleAction.setType(eChild3.getAttributeValue(TMP_TYPE));
									
									lChild4 = eChild3.getChildren();
									if (lChild4 != null && lChild4.size() > 0) {
										it4 = lChild4.iterator();
										while (it4.hasNext()) {
											eChild4 = it4.next();
											if (eChild4.getName().equals(TMP_PROPERTY)) {
												log.debug("Adicionando <property> "+eChild4.getAttributeValue(TMP_NAME));
												cfgProperty = new CfgProperty(eChild4.getAttributeValue(TMP_NAME));
												cfgHandleAction.addProperty(cfgProperty);
												
												if (eChild4.getAttributeValue(TMP_VALUE) != null) {
													cfgProperty.setValue(eChild4.getAttributeValue(TMP_VALUE));
												}
												
												lChild5 = eChild4.getChildren();
												if ((lChild5 == null || lChild5.size() == 0) && eChild4.getText() != null && eChild4.getText().length() > 0) {
													if (cfgProperty.getValue() != null && cfgProperty.getValue().length() > 0) {
														log.warn("Foi definido um atributo 'value' com valor no corpo do node <"+TMP_PROPERTY+"> de "+cfgProperty.getName());
													}
													cfgProperty.setValue(eChild4.getText());
												}
											}
										}
									} else if (eChild3.getText() != null && eChild3.getText().length() > 0) {
										cfgHandleAction.setValue(eChild3.getText());
									}
									
									//if ((cfgHandleAction.getValue() == null || cfgHandleAction.getValue().length() == 0) && (cfgHandleAction.getPropertyList() == null || cfgHandleAction.getPropertyList().size() == 0)) {
									if (cfgHandleAction.getValue() == null || cfgHandleAction.getValue().length() == 0) {
										cfgHandleAction.setValue(eChild3.getAttributeValue(TMP_VALUE));
									}
								}
								
							}
						}
					}
				}
			}
		}
		log.debug("Carregado a configuracao: "+cfgIFlowStruts);
		StrutsFlowDriver.cfgIFlowStruts = cfgIFlowStruts;
	}


}
